package com.gt.quality.config.aspectj;

import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * @author makeJava
 */
@Aspect
@Component
@Order(1)
public class InterfaceDetailAspect {

    Logger logger = LoggerFactory.getLogger(InterfaceDetailAspect.class);

    // 定义切点，匹配所有控制器类的公共方法
    @Pointcut("execution(public * com.gt.quality.system.controller..*(..))")
    public void controllerMethods() {
    }

    // 通过环绕通知（Around Advice）来记录入参、出参和执行时间
    @Around("controllerMethods()")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        // 跳过Set注入bean的方法
        Signature signature = joinPoint.getSignature();
        String methodName = signature.getName();
        if (methodName.startsWith("set")) {
            return joinPoint.proceed();
        }
        // 如果方法上面有@Autowired注解，则不记录入参和出参
        MethodSignature methodSignature = (MethodSignature) signature;
        String simpleClassName = joinPoint.getTarget().getClass().getSimpleName();
        // 获取方法对象
        java.lang.reflect.Method method = methodSignature.getMethod();
        // 获取方法上的注解
        Annotation[] annotations = method.getAnnotations();
        boolean hasAutowiredAnnotation = false;
        for (Annotation annotation : annotations) {
            if (annotation instanceof Autowired || annotation instanceof Resource) {
                hasAutowiredAnnotation = true;
                break;
            }
        }
        // 如果方法上面有@Autowired注解，则不记录入参和出参
        if (hasAutowiredAnnotation) {
            return joinPoint.proceed();
        }
        long startTime = System.currentTimeMillis();
        Object[] args = joinPoint.getArgs();
        List<Object> argList = new ArrayList<>();
        for (Object arg : args) {
            if (arg instanceof MultipartFile) {
                argList.add("MultipartFile");
            } else if (arg instanceof HttpServletRequest) {
                argList.add("HttpServletRequest");
            } else if (arg instanceof HttpServletResponse) {
                argList.add("HttpServletResponse");
            } else {
                argList.add(arg);
            }
        }
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        String header1 = request.getHeader("token");
        String header2 = request.getHeader("Authorization");
        logger.info("\n ------>接口处理类:{};\n ------>调入方法: {};\n ------>传入参数: {};\n ------>header|token:{},\n ------>header|Authorization:{}", simpleClassName, methodName, argList, header1 , header2);

        // 执行目标方法
        Object result = joinPoint.proceed();

        // 计算方法执行时间
        long endTime = System.currentTimeMillis();
        long duration = endTime - startTime;
        String response = Optional.ofNullable(result).orElse("Void").toString();
        if (response.length() > 2048) {
            response = response.substring(0, 2048).concat(".........");
        }
        // 记录出参和耗时
        logger.info(" \n ---->接口处理类: {}.{};  \n=======>返回报文: {}; \n=======>执行耗时: {} ms; \n------Ending-----", simpleClassName,methodName, response, duration);
        return result;
    }

    // 异常通知
    @AfterThrowing(pointcut = "controllerMethods()")
    public void exceptionTry(JoinPoint joinPoint) throws Throwable {
        // 跳过Set注入bean的方法
        Signature signature = joinPoint.getSignature();
        String methodName = signature.getName();
        String simpleClassName = signature.getClass().getSimpleName();
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        long startTime = System.currentTimeMillis();
        Object[] args = joinPoint.getArgs();
        List<Object> argList = new ArrayList<>();
        for (Object arg : args) {
            if (arg instanceof MultipartFile) {
                argList.add("MultipartFile");
            } else if (arg instanceof HttpServletRequest) {
                argList.add("HttpServletRequest");
            } else if (arg instanceof HttpServletResponse) {
                argList.add("HttpServletResponse");
            } else {
                argList.add(arg);
            }
        }
        String header = request.getHeader("header");
        logger.error("{}--->Method: {}, Args: {},header:{}", simpleClassName, methodName, argList, header);

        long endTime = System.currentTimeMillis();
        long duration = endTime - startTime;
        // 记录出参和耗时
        logger.info("{}Begin-------\n=======>接口: {}\n=======>入参 : {} \n=======>执行方法: {}\n=======>返回报文: {}\n=======>执行耗时: {} ms\nEnding-----", simpleClassName, request.getRequestURI(), Arrays.toString(args), methodName, "response", duration);
    }
}

